Scrapbox to Markdown 文法変換の設計やら実装やら
impl4
各行の文法変換 with たぶん正規表現ゲー
どうしよう
答えは既に先人がたくさんつくってるけど
あえて自分で書いてみるか
code:py
def scb_to_markdown_in_line(line):
return line
ここまではつくった
あとはこの中を増やしていくだけ
やっぱ正規表現しかないよなぁ
n回の出現を全部replaceしたいもの
import reの使い方いっつも迷ってる
jsからアクセスしてPython選んだらいけるようになった
意味不明直った
こっちでやりまふ
ok
最後に<br>が入る問題は?
last blank lineの違いだった
https://gyazo.com/e45eff19dd2ffe18da9d1ac219ca3293
152行目
これがあると入る
ないと完全一致することを確認した
今はテストデータに入れてるので、expect側もbr入れるべきか
impl3
行指向の変換アルゴリズム、大体掴んだので次こそ実装していくよー
行判定部分つくりこんでる
デバッグで辿り着ける自信がないので足元から固めていく作戦
sta.iconこの後が重いなー
がー、状態ちゃんと設計しないとダメそう
オートマトン書いた方がいいかな
状態
s: start
in-P: in paragraph
in-L: in list
in-B: in block
遷移(矢印)
b: blankline 現在行は空行です
p: 段落です
l: リストです(深さ変わってません)
l+: リストです(深さ増えました)
l-: リストです(深さ減りました)
c: codeの始まりです(リストかもしれないし段落かもしれない)
t: tableの始まりです(リストかもしれないし段落かもしれない)
せっかくだからDrawingで書いてみるか
https://gyazo.com/d5d2c6327e5075bce45c1822b6056976
とりあえず状態が足らん(l-遷移を二箇所書くという二歩みたいなことしてるw
in-L に in-L(1) と in-L(2+) が要る
in-B に in-B(1) と in-B(2+) が要る
EOFに来た時(次行がない)も考慮要
train.iconくそしんどい
今回は上手く入ったけど、レイアウト的に入らない場合に書き直しになってしまう
そして俺には頭の中でレイアウトのあたりをつける能力はない
ので実質運ゲー
オートマトンをどうやってコードに落とせばいいかもわからん
Stateパターンとか?
愚直に実装すればいいよな
実装するからにはちゃんと図つくってそのとおりに実装しないと後で死ぬ
整合性が取れないとコード愚直に読んでデバッグするゲーになってしまう
待った
これ、状態なんか導入しなくても「インデントの深さ」一つだけ持っておけば済まない?
要するにインデントの変化を見ればいい(N>=2とする)
0 to 1
0 to Nややこしいのでいったん考えない
1 to 0
N to 0
1 to N
N to 1
N to N+1
N to N-1
いや、「インデントの深さ」と「空行」の二つだな(N>=1とする)
0 to N
N to 0
N to N+1
N to N-1
再載
scbの段落行の前後には \n を入れる
scbの段落行と段落行の間には \n が必要
scbの段落行とリスト行の間にも \n が必要
空行は<br>\nに置換する
リスト行が終わった場合は \n が必要
(リスト行の次が空行だった場合は実質\n<br>\nとなる)
sta.iconんー、スマートに全体像掴む見方が浮かばん
最悪てきとーにつくりながら手探りで潰していく、だけど突貫的になるのでできればやめたい
空行解釈とインデント解釈を分けた方が良い気がする
input: scblines[]
output: markdownlines[]
まずは空行解釈する
空行は <br>\n にする( ['<br>', ''])
インデント解釈では
追加挿入のバリエーションだけ洗い出しておく
---
追加しない
ブロックの終わりを挿入する(['```'])
\nを挿入する(['\n'])
↑ うん、だいぶシンプルになった
追加挿入が計4パターン(1+3)しかない
書いていきやしょ
step1: 空行の処理
input: scblines[]
output: outlines[]
scblineのうち、空行部分を['<br>', '']に置換したもの
step2: インデントの深さ変更に伴う挿入処理
input: lines[]
output: outlines[]
以下が余分に挿入されている
\n
ブロックの終わり
こいつはprevを必要とする
step3: 各行の置換
input: lines[]
output: markdownlines[]
文字装飾系(これは正規表現が良い)
書いてく
python scbjson2ghpages.py -i testdata-for-to-markdown.json > actual_page.md
これでexpect_page.mdと見比べながら泥臭く直していく
def judge_extra_insertion(cls, mode_of_prevline, mode_of_curline):
モードじゃなくて「インデントの深さ」が良さそう(昨日の考察)
ただし<br>はすでに入ってるので無視してねー
★1
リストやテーブルの場合、add \n で良い
コードブロックの場合、add ```
★2
リストの場合、ingnore
ブロックの場合、かつ start line のインデントより深い場合、ignore
ブロックの場合、かつ start line のインデント以下の場合、sta.icon*3このへんちょっと短期記憶追いつかん。。。
table:patterns
curlineのインデント数 prevlineのインデント数 どう解釈すべき? 備考
0 0 ignore 空行または段落が続いている
0 1 ★1 リスト or ブロックがおわた
0 2+ ★1 リスト or ブロックがおわた
1 0 add \n リスト or ブロックがはじまた
1 1 ignore リスト or ブロックが続いてる
1 2+ ignore リストが深くなった or ブロックが続いている
2+ 0 ★1 リスト or ブロックがおわた
2+ 1 ★2 リストが浅くなった or ブロックが続いてる or ブロックがおわた
2+ 2+ ignore リスト or ブロックが続いている or 深くなった
★1にせよ★2にせよ
以下がいる
現在特定のブロックに入っているかどうか
入っている場合、何のブロックか(code or table)
入っている場合、そのスタート時点のインデントの深さ
以上を踏まえると、
bf: def judge_extra_insertion(cls, mode_of_prevline, mode_of_curline):
af: def judge_extra_insertion(cls, prev_indentdepth, cur_indentdepth, inblock_state):
class InBlockState
is_in_block
mode
indentdepth_of_start
あとは上記テーブル分の条件分岐をひたすら並べるだけだー
綺麗に書きたい誘惑があるが、いったんあとで
step2の一部まで実装してみた
https://gyazo.com/aefc3348957090544542361011f34781
条件分岐の羅列!
ガード節でせめてリーダブルにするくらいしか思いつかなかった けど、全然expectに一致しないsta.icon*3
根気強くデバッグしていくフェーズ突入。。。
step1時点のoutも欲しいかもなsta.icon
https://gyazo.com/65e0042988469344565099857807c22a
step1で [''] を ['<br>', '']にしている(下方向にだけ空行入れてる)
っつーことは、全過程において「下方向に挿入する」で統一性持たせないといけない
たぶんどっかで統一ずれてる
or step1 で['', '<br>', '']みたいに前後に空行入れてしまうか
修正ok
が、新たな問題が
prev indent depth と current indent depthだけで状態遷移網羅できる設計だが、そんなことはなかった
空行かどうか、の prev と current も要る
step1, 2に分けたの間違いだったかしら……
メタで処理することで回避
https://gyazo.com/3f0b41d9e911d776a1316e818d814063
★1の実装
inblockstate使います
inblockstateだけだときついのでwrapperつくった
https://gyazo.com/fdc04a0eb426149dcbcccf4e580488ee
テストコードはあるからホワイトボックス的な挙動確認はできる
ただ中身を理解しようとすると苦労しそうな気がする
sta.icon「未来の僕へ。たぶんコードだけで理解するのきついんで、このscrapboxページ読んで設計思い出してちょ」
ok、だいぶ一致してきた
https://gyazo.com/767b63b3c2d1d253a73b3735b82c016d
★2の実装
テストデータまだだから確認できてないけど、実装はおわた
impl2
実装していくよー
境界という概念がある
インデントが一段上がった → 記法のおわり
コードブロック
テーブル
空行の意味
露骨な例
code:scb
段落
リスト
リスト
リスト
code:対応するmarkdown
段落
<br>
- リスト
- リスト
<br>
- リスト
塊
code:scb
段落
リスト
リスト
段落
code:md
段落
- リスト
<br>
- リスト
段落
段落のバリエーション
code:scb
段落1
段落2
段落3
段落4
段落5
code:md
段落1
段落2
<br>
段落3
段落4
段落5
リストのバリエーション
code:scb
リスト
リスト
リスト
リスト
リスト
code:md
- リスト
- リスト
<br>
- リスト
- リスト
<br>
- リスト
空行のバリエーション
code:scb
段落
段落 差1
段落 差2
段落 差3
code:結論.md
段落
<br>
段落 差1
<br>
<br>
段落 差2
<br>
<br>
<br>
段落 差3
つまり?
scbの段落行の前後には \n を入れる
scbの段落行と段落行の間には \n が必要
scbの段落行とリスト行の間にも \n が必要
空行は<br>\nに置換する
リスト行が終わった場合は \n が必要
(リスト行の次が空行だった場合は実質\n<br>\nとなる)
変換時には状態という概念がある
つまりn行目の変換はn行目の内容だけで完結するとは限らない
n-1行目、n+1行目の内容が必要(なことがある)
もっというと
ここまでのパース時の状態を保持しておく必要がある
たとえば「今はリストをパース中でーす」とか
次行が何の行かを見に行かねばならないかもしれない
例浮かばないけど
モード
ある行が何を表すか
例: 空行、段落、リスト(indent=1)、リスト(indent=2)、codeblock開始、table開始 etc
状態
prev mode …… 前行のモード
current mode …… 現在行のモード
prevとcurrentを使えば、変換後に挿入する追加処理も決まる
表でまとめてみる
空行、段落、リスト1、リスト2、codeblockの5つでいいか
table:状態遷移?
prev current 行の間に何が必要か(何も必要ない、は - で示す)
段落 段落 \n
段落 空行 \n
段落 リストn \n
段落 codeblock \n
空行 段落 \n
空行 空行 \n
……
リスト 空行 \n
リスト 段落 \n
リスト1 リスト2 -
リスト2 リスト1 -
リストi リストi -
リスト codeblock -
codeblock
あれ、これほぼ\nじゃね?
むしろ\nじゃないのってなに?
連結してる文法
リスト
codeblock内
table内
上に追加する
あー、codeblockやtableの中に、さらにリストがあるんだ
「リスト in the block」みたいなモードがある
indent=3でスタートすると、リスト in the blockモードは「indent=3以下」の行にぶち当たるまで続く
sta.iconぶちあたった時に何挿入するかで分岐があるな
スタート時点でリストに入ってた場合、何も挿入しなくていい
が、markdownではそもそもリスト中のブロックなんて概念がないから、これはできない
Q: リスト中のブロックという表現を見かけたら、どう変換するの?
インデント殺してゼロインデントで表現するしかないかー
把握した
impl1
sbq直した
https://gyazo.com/452a2ccd6cea13bf7f82171a167ae2d7
既にsbqでProjectやPageといったクラスがある
to markdownは、Page.to_markdown() みたいに実装してやればよい
変換処理どうしようかな
正規表現がベターなんだろうけど
既にsbq parserが行指向になってしまっている……
https://gyazo.com/777cf34172725fb255eaf37c28ed2752
https://gyazo.com/c826bdb27383e5f11feae60d116cb1bc
この違いを Markdown にどう反映させるか、とか
あと正規表現読みづらいから嫌い感ある
でも使っていかないと慣れないよなぁ
プログラマ名乗りたいならサクサク読めなきゃいけない
折衷案だな
コードリーディングはあとでちゃんとやって、勉強ノートもメモして正規表現鍛える
こっちでは行指向で実行しましょ
ただし行指向つらそうだったら正規表現も考える
先にページpageのexpect dataつくってみる
左にmarkdownエディタ、右にScrapbox
sta.icon悪くないし、普通に便利ですね
足りない
空行をはさんだリスト-リスト
expect
https://gyazo.com/c047910247e279883a8173ae1ddf5998
actual
https://gyazo.com/6c8b24f984905e3fd756e7e7529c78ea
連結されてしまう。。。
リスト-段落の空行ありなしの差がわからない
https://gyazo.com/5b3a5bf745af7ecb1f04ef1d6eac2669
そもそもMarkdownは<p>で区切るゲー(段落ゲー)なので、Scrapboxみたいな行<br>- リストみたいな表現を持っていない
よくわからんバグ done from 2
https://gyazo.com/b17b882daab1861caa9720960939fd00
ちゃんと書いてますけど
https://gyazo.com/3aa0a2060d1bb83d1a5695ffc7d5062c
これはいいか(10以上ネストするケースはScrapboxでもそうはない(合ったとしてもそれは切り出しなどするべきって話
ファイル名として使えない文字を含んだタイトルへのリンク
+page+とか(+page+.mdなんてファイルはつくれない(Windowsでは+は使えない
---
絵文字で対処するか?
最大の懸念だったここを潰せたのでokかな